#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "analysis.h"

// create tree from logfile
hcct_tree_t* createTree(FILE* logfile) {    
    char buf[BUF+1];
    char *s;
    
    hcct_tree_t *tree=malloc(sizeof(hcct_tree_t));
        
    // Syntax of the first two rows
    // c <tool> [<epsilon> <phi>] [<sampling_interval> <burst_length> <burst_enter_events>]
    // c <command> <process/thread id> <working directory>
    
    // First row
    if (fgets(buf, BUF, logfile)==NULL) {
		printf("Error: broken logfile\n");
		exit(1);
	}
    s=strtok(buf, " ");
    if (strcmp(s, "c")) {
        printf("Error: wrong format!\n");
        exit(1);
    }
    s=strtok(NULL, " ");
        
    if (strcmp(s, CCT_FULL_STRING)==0) {	
        tree->tool=CCT_FULL;        
        tree->burst_enter_events=0;
        printf("Log generated by tool: %s", CCT_FULL_STRING); // CCT_FULL_STRING ends by \n
    } else if (strcmp(s, LSS_FULL_STRING)==0) {
        tree->tool=LSS_FULL;
        tree->burst_enter_events=0;
        s=strtok(NULL, " ");        
        tree->epsilon=strtoul(s, &s, 0);
        s++;
        tree->phi=strtoul(s, &s, 0);
        printf("Log generated by tool: %s\n", LSS_FULL_STRING);
        printf("Parameters: epsilon=%lu phi=%lu\n", tree->epsilon, tree->phi);        
    } else if (strcmp(s, CCT_BURST_STRING)==0) {
        tree->tool=CCT_BURST;
        s=strtok(NULL, " ");        
        tree->sampling_interval=strtoul(s, &s, 0);
        s++;
        tree->burst_length=strtoul(s, &s, 0);
        s++;
        tree->burst_enter_events=strtoull(s, &s, 0);
        printf("Log generated by tool: %s\n", CCT_BURST_STRING);
        printf("Parameters: sampling_interval=%lu burst_length=%lu burst_enter_events=%llu\n",
                tree->sampling_interval, tree->burst_length, tree->burst_enter_events);
    } else if (strcmp(s, LSS_BURST_STRING)==0) {
        tree->tool=LSS_BURST;
        s=strtok(NULL, " ");        
        tree->epsilon=strtoul(s, &s, 0);
        s++;
        tree->phi=strtoul(s, &s, 0);
        s++;
        tree->sampling_interval=strtoul(s, &s, 0);
        s++;
        tree->burst_length=strtoul(s, &s, 0);
        s++;
        tree->burst_enter_events=strtoull(s, &s, 0);
        printf("Log generated by tool: %s\n", LSS_BURST_STRING);
        printf("Parameters: epsilon=%lu phi=%lu sampling_interval=%lu burst_length=%lu burst_enter_events=%llu\n",
                tree->epsilon, tree->phi, tree->sampling_interval, tree->burst_length, tree->burst_enter_events);
    }
    
    // Second row
    if (fgets(buf, BUF, logfile)==NULL) {
		printf("Error: broken logfile\n");
		exit(1);
	}
    s=strtok(buf, " ");
    if (strcmp(s, "c")) {
        printf("Error: wrong format!\n");
        exit(1);
    }
    s=strtok(NULL, " ");
    printf("Instrumented program:");
    while (s!=NULL) {
        printf(" %s", s);
        s=strtok(NULL, " ");
    }

    // I need a stack to reconstruct the tree
    hcct_pair_t tree_stack[STACK_MAX_DEPTH];
    int stack_idx=0;
    UINT32 nodes=0;
    
    UINT32 node_id, parent_id, counter, routine_id;
    UINT16 call_site;
        
    // Create root node
    hcct_node_t* root=malloc(sizeof(hcct_node_t));    
    
    if (fgets(buf, BUF, logfile)==NULL) {
		printf("Error: broken logfile\n");
		exit(1);
	}
    s=strtok(buf, " ");        
    if (strcmp(s, "v")) {
            printf("Error: wrong format!\n");
            exit(1);
    }
    
    tree_stack[0].node=root;    
    tree_stack[0].id=strtoul(&buf[2], &s, 16); // node_id
    s++; // skip " " character
    if (strtoul(s, &s, 16)!=0) { // parent_id should be 0
            printf("Error: root node cannot have a parent node!\n");
            exit(1);
    }
    s++;
    root->parent=NULL;
    root->first_child=NULL;
    root->next_sibling=NULL;
    root->counter=strtoul(s, &s, 0);    
    s++;
    root->routine_id=strtoul(s, &s, 16);
    s++;
    root->call_site=(unsigned short)strtoul(s, &s, 16);
    if (root->counter!=1 || root->routine_id!=0 || root->call_site!=0) {
            printf("Error: there's something strange in root node data (counter, routine_id or call_site)!\n");
            exit(1);
    }
    nodes++;
        
    // Syntax: v <node id> <parent id> <counter> <routine_id> <call_site>    
    hcct_node_t *node, *parent, *tmp;
    while(1) {
        if (fgets(buf, BUF, logfile)==NULL) {
			printf("Error: broken logfile\n");
			exit(1);
		}
        s=strtok(buf, " ");        
        if (strcmp(s, "p")==0) break;
        if (strcmp(s, "v")) {
            printf("Error: wrong format!\n");
            exit(1);
        }
        
        node=malloc(sizeof(hcct_node_t));
        node_id=strtoul(&buf[2], &s, 16);
        s++;    
        parent_id=strtoul(s, &s, 16);
        s++;
        node->counter=strtoul(s, &s, 0);
        s++;
        node->routine_id=strtoul(s, &s, 16);
        s++;
        node->call_site=(unsigned short)strtoul(s, &s, 16);
        
        // Attach node to the tree
        while (tree_stack[stack_idx].id!=parent_id && stack_idx>=0)
            stack_idx--;
        if (tree_stack[stack_idx].id!=parent_id) {
            printf("Error: log file is broken - missing node(s)\n");
            exit(1);            
        }        
        parent=tree_stack[stack_idx].node;
        node->parent=parent;
        node->first_child=NULL;
        node->next_sibling=NULL;
        if (parent->first_child==NULL)
            parent->first_child=node;
        else {
            // Attach as rightmost child
            for (tmp=parent->first_child; tmp->next_sibling!=NULL; tmp=tmp->next_sibling);
            tmp->next_sibling=node;                        
        }
        stack_idx++;
        tree_stack[stack_idx].node=node;
        tree_stack[stack_idx].id=node_id;
        nodes++;
    }
    
    // Syntax: p <num_nodes>
    tree->nodes=strtoul(&buf[2], &s, 0);
    if (tree->nodes!=nodes) { // Sanity check
        printf("Error: log file is broken - wrong number of nodes\n");
        exit(1);
    }        
    s++;
    tree->enter_events=strtoull(s, NULL, 0);
    
    printf("Total number of nodes: %lu\n", tree->nodes);
    printf("Total number of rtn_enter_events: %llu\n", tree->enter_events);
    
    tree->root=root;
    
    
    printf("Tree has been built.\n");
    
    return tree;
}

void freeTreeAux(hcct_node_t* root) {
    hcct_node_t *ptr;
    for (ptr=root->first_child; ptr!=NULL; ptr=ptr->next_sibling)
        freeTreeAux(ptr);
    free(root);
}

void freeTree(hcct_tree_t* tree) {
    if (tree->root!=NULL)
        freeTreeAux(tree->root);
    free(tree);
    printf("Tree has been deleted from memory.\n");
}

// ---------------------------------------------------------------------
//  routines adapted from metrics.c (PLDI version)
// ---------------------------------------------------------------------
UINT32 hotNodes(hcct_node_t* root, UINT32 threshold) {
    UINT32 h=0;
    hcct_node_t* child;
    for (child=root->first_child; child!=NULL; child=child->next_sibling)
        h += hotNodes(child,threshold);
    if (root->counter >= threshold) {
            // printf per stampa a video??
            return h+1; 
    }
    else return h;
}

UINT64 sumCounters(hcct_node_t* root) {
    UINT64 theSum=(UINT64)root->counter;
    hcct_node_t* child;
    for (child=root->first_child; child!=NULL; child=child->next_sibling)
        theSum += sumCounters(child);
    return theSum;
}

UINT32 hottestCounter(hcct_node_t* root) {
    UINT32 theHottest=root->counter;
    hcct_node_t* child;
    for (child=root->first_child; child!=NULL; child=child->next_sibling)  {
        UINT32 childHottest = hottestCounter(child);
        if (theHottest<childHottest) theHottest=childHottest;
    }
    return theHottest;
}

UINT32 largerThanHottest(hcct_node_t* root, UINT32 TAU, UINT32 hottest) {
    UINT32 num=0;
    hcct_node_t* child;
    if (root->counter*TAU >= hottest) num++;
    for (child=root->first_child; child!=NULL; child=child->next_sibling) 
        num += largerThanHottest(child,TAU,hottest);
    return num;
}
// ---------------------------------------------------------------------
//  end of routines adapted from metrics.c (PLDI version)
// ---------------------------------------------------------------------


int main(int argc, char* argv[]) {
    if (argc!=2) {
        printf("Syntax: %s <logfile>\n", argv[0]);
        exit(1);
    }
    FILE* logfile;
    logfile=fopen(argv[1], "r");
    if (logfile==NULL) {
        printf("The specified file does not exist!\n");
        exit(1);
    }
    hcct_tree_t *tree;
    tree=createTree(logfile);
    UINT32 hot=hotNodes(tree->root, 100);
    printf("Number of hot nodes (counter>100): %lu\n", hot);
    UINT64 sum=sumCounters(tree->root);
    printf("Sum of counters: %llu\n", sum);
    UINT32 hottest=hottestCounter(tree->root);
    printf("Hottest counter: %lu\n", hottest);
    UINT32 closest=largerThanHottest(tree->root, 10, hottest); // TAU=10
    printf("Hottest nodes for TAU=10: %lu\n", closest);
    fclose(logfile);
    freeTree(tree);
    return 0;
}
